//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjExample01.h"
#include "NjEventTest.h"
#include "NjSynchronizationTest.h"
#include "NjSharedSyncObjectTest.h"
#include "NjDebugSystemTestImplementation.h"
#include "NjSharedSyncObjectConstants.h"
#include "NjThreadsSystem.h"
#include "NjTestUnit.h"
#include "NjRandUint.h"
#include "NjExampleRandGenerator.h"
#include "NjMemUtils.h"
#include "NjPlatformSyncObject.h"
#include "NjHighResolutionTimer.h"
#include "NjPlacementNew.h"

//----------------------------------------------------------------------------
class NjEventTest::Static
{
public:
	typedef NjSharedSyncObjectConstants::Query<NjEvent> Constants;

	static void RunEventFunctional();
	static void RunEventFunctionalMultithreaded();
	static void RunEventPerformances();
	static void RunEventAssert();
	static void RunEventFunctionalThread(void * pData);
	static void RunEventAssertThread(void * pData);

	enum ErrorState
	{
		STATUS_SUCCESS,
		NOT_INITIALIZED
	};

	class EventFunctionalSharedData
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(EventFunctionalSharedData);
		NJ_TRIVIAL_DESTRUCTOR(EventFunctionalSharedData);

		static const NjUint TOT_THREADS = 20;
		static const NjUint TOT_EVENTS = 5;
		static const NjUint ITERATIONS = 100*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
		static const NjUint TOT_ENTRIES = ITERATIONS*TOT_THREADS*2;

		class ThreadData
		{
		public:
			NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(ThreadData);
			NJ_TRIVIAL_DESTRUCTOR(ThreadData);

			EventFunctionalSharedData * m_pSharedData;
			NjThreadsSystem::Thread * m_pThread;
			ErrorState m_eErrorState;

			NJ_DISABLE_COPY(ThreadData);
		};

		NjEvent * m_aEvent[TOT_EVENTS];
		ThreadData m_aThreadsData[TOT_THREADS];

		enum EventState
		{
			TRY_WAIT_START,
			TRY_WAIT_END,
			TRY_WAIT_FAIL,
			SET_EVENT_START,
			SET_EVENT_END,
			UNSET_EVENT_START,
			UNSET_EVENT_END
		};

		class Entry
		{
		public:
			NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(Entry);
			NJ_TRIVIAL_DESTRUCTOR(Entry);

			NjUint m_uEvent;
			EventState m_eEventState;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			NjUint8 m_aSnapshot[sizeof(NjEvent)];
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			ThreadData * m_pThreadData;

			NJ_DISABLE_COPY(Entry);
		};

		Entry m_aEntries[TOT_ENTRIES];
		NjAtomic<NjUint> m_kTotEntries;

		NJ_DISABLE_COPY(EventFunctionalSharedData);
	};

	class SharedData02
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(SharedData02);
		NJ_TRIVIAL_DESTRUCTOR(SharedData02);

		NjSemaphore m_kWaitLock;
		NjSemaphore m_kStartUnlock;
		NjPlatformSyncObject::SyncObject * m_pPlatformSync;
		NjUint m_uTotal;

		static void ThreadFunct(void * pData);
	};

	NJ_DISABLE_INSTANCE(Static);
};
//----------------------------------------------------------------------------
void NjEventTest::Run(void)
{
	Static::RunEventFunctional();
	Static::RunEventFunctionalMultithreaded();
	Static::RunEventPerformances();
	Static::RunEventAssert();
}
//----------------------------------------------------------------------------
void NjEventTest::Static::RunEventFunctional()
{
	NJ_TEST_UNITS_GROUP(Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Constructors, Destructors, Set, Unset, TryWait");
			NjEvent kEventOff(NJ_FALSE);
			NjEvent kEventOn(NJ_TRUE);

			NJ_TEST(!kEventOff.TryWait(NjEventsSystem::NO_WAIT));
			NJ_TEST(kEventOn.TryWait(NjEventsSystem::NO_WAIT));

			kEventOn.Unset();
			kEventOff.Set();

			NJ_TEST(kEventOff.TryWait(NjEventsSystem::NO_WAIT));
			NJ_TEST(!kEventOn.TryWait(NjEventsSystem::NO_WAIT));

			kEventOn.Set();
			kEventOff.Unset();

			NJ_TEST(!kEventOff.TryWait(NjEventsSystem::NO_WAIT));
			NJ_TEST(kEventOn.TryWait(NjEventsSystem::NO_WAIT));
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
void NjEventTest::Static::RunEventFunctionalMultithreaded()
{
	NJ_TEST_UNITS_GROUP(Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Constructors, Destructors, Set, Unset, TryWait");
			EventFunctionalSharedData * const pSharedData(NjNew(EventFunctionalSharedData));

			pSharedData->m_kTotEntries.Set(0);

			NjEvent * * pEventPos=pSharedData->m_aEvent;
			NjEvent const * const * const pEventEnd=pEventPos+EventFunctionalSharedData::TOT_EVENTS;

			while (pEventPos<pEventEnd)
			{
				(*pEventPos)=NjNew(NjEvent(NJ_TRUE));
				++pEventPos;
			}

			EventFunctionalSharedData::ThreadData * pPos(pSharedData->m_aThreadsData);
			EventFunctionalSharedData::ThreadData * const pEnd(pPos+EventFunctionalSharedData::TOT_THREADS);

			while (pPos!=pEnd)
			{
				EventFunctionalSharedData::ThreadData & kThreadData(*pPos);
				++pPos;

				kThreadData.m_pSharedData=pSharedData;
				kThreadData.m_pThread=NjThreadsSystem::CreateThread(RunEventFunctionalThread, &kThreadData);

				kThreadData.m_eErrorState=NOT_INITIALIZED;

				NJ_TEST(kThreadData.m_pThread!=NJ_NULL);
			}

			pPos=pSharedData->m_aThreadsData;

			while (pPos!=pEnd)
			{
				EventFunctionalSharedData::ThreadData & kThreadData(*pPos);
				++pPos;

				NJ_TEST(NjThreadsSystem::TryJoinThread(kThreadData.m_pThread, (pEnd+1-pPos)*EventFunctionalSharedData::ITERATIONS*NjSharedSyncObjectConstants::TIME_GRANULARITY)==NjEventsSystem::TRY_SUCCESS);
				NJ_TEST(NjThreadsSystem::DestroyThread(kThreadData.m_pThread));
				NJ_TEST(kThreadData.m_eErrorState==STATUS_SUCCESS);
			}

			EventFunctionalSharedData::Entry const * pEntry(pSharedData->m_aEntries);
			EventFunctionalSharedData::Entry const * const pEntryEnd(pEntry+pSharedData->m_kTotEntries.Get());

			enum EventState
			{
				EVENT_STATE_UNSET,
				EVENT_STATE_SET,
				EVENT_STATE_UNKNOWN,
				EVENT_STATE_UNKNOWN_CAN_RESET,
			};

			struct EventData
			{
				NjUint m_uTotSetting;
				NjUint m_uTotUnsetting;
				EventState m_eEventState;
				NjUint m_uSetConter;
				NjUint m_uUnsetConter;
			};

			struct ThreadData
			{
				EventState m_eEventState;
				NjUint m_uSetConter;
				NjUint m_uUnsetConter;
			};

			EventData aEventState[EventFunctionalSharedData::TOT_EVENTS];
			EventData * pEventStatePos(aEventState);
			EventData const * const pEventStateEnd(aEventState+NJ_LENGTH_OF(aEventState));
			while (pEventStatePos!=pEventStateEnd)
			{
				pEventStatePos->m_uTotSetting=0;
				pEventStatePos->m_uTotUnsetting=0;
				pEventStatePos->m_eEventState=EVENT_STATE_SET;
				pEventStatePos->m_uSetConter=0;
				pEventStatePos->m_uUnsetConter=0;
				++pEventStatePos;
			}

			ThreadData aThreadState[EventFunctionalSharedData::TOT_THREADS];
			ThreadData * pThreadStatePos(aThreadState);
			ThreadData const * const pThreadStateEnd(aThreadState+NJ_LENGTH_OF(aThreadState));
			while (pThreadStatePos!=pThreadStateEnd)
			{
				pThreadStatePos->m_uSetConter=0;
				pThreadStatePos->m_uUnsetConter=0;
				pThreadStatePos->m_eEventState=EVENT_STATE_SET;
				++pThreadStatePos;
			}

			while (pEntry<pEntryEnd)
			{
				NjUint const uThread(pEntry->m_pThreadData-pSharedData->m_aThreadsData);
				NJ_TEST(uThread<EventFunctionalSharedData::TOT_THREADS);

				NjUint const uEvent(pEntry->m_uEvent);
				NJ_TEST(uEvent<EventFunctionalSharedData::TOT_EVENTS);

				switch(pEntry->m_eEventState)
				{
					case EventFunctionalSharedData::TRY_WAIT_START:
						aThreadState[uThread].m_eEventState=aEventState[uEvent].m_eEventState;
						aThreadState[uThread].m_uSetConter=aEventState[uEvent].m_uSetConter;
						aThreadState[uThread].m_uUnsetConter=aEventState[uEvent].m_uUnsetConter;
						break;

					case EventFunctionalSharedData::TRY_WAIT_END:
						NJ_TEST(aThreadState[uThread].m_eEventState!=EVENT_STATE_UNSET || aEventState[uEvent].m_eEventState!=EVENT_STATE_UNSET || aThreadState[uThread].m_uSetConter!=aEventState[uEvent].m_uSetConter);
						break;

					case EventFunctionalSharedData::TRY_WAIT_FAIL:
						NJ_TEST(aThreadState[uThread].m_eEventState!=EVENT_STATE_SET || aEventState[uEvent].m_eEventState!=EVENT_STATE_SET || aThreadState[uThread].m_uUnsetConter!=aEventState[uEvent].m_uUnsetConter);
						break;

					case EventFunctionalSharedData::SET_EVENT_START:
					{
						if (aEventState[uEvent].m_uTotSetting==0 && aEventState[uEvent].m_uTotUnsetting==0 &&
							(aEventState[uEvent].m_eEventState==EVENT_STATE_UNKNOWN || aEventState[uEvent].m_eEventState==EVENT_STATE_UNSET))
							aEventState[uEvent].m_eEventState=EVENT_STATE_UNKNOWN_CAN_RESET;
						else if (aEventState[uEvent].m_uTotUnsetting>0)
							aEventState[uEvent].m_eEventState=EVENT_STATE_UNKNOWN;

						++aEventState[uEvent].m_uSetConter;
						++aEventState[uEvent].m_uTotSetting;
						break;
					}

					case EventFunctionalSharedData::SET_EVENT_END:
					{
						NJ_TEST(aEventState[uEvent].m_uTotSetting>0);
						--aEventState[uEvent].m_uTotSetting;

						if (aEventState[uEvent].m_eEventState==EVENT_STATE_UNKNOWN_CAN_RESET)
							aEventState[uEvent].m_eEventState=EVENT_STATE_SET;
						break;
					}

					case EventFunctionalSharedData::UNSET_EVENT_START:
					{
						if (aEventState[uEvent].m_uTotSetting==0 && aEventState[uEvent].m_uTotUnsetting==0 &&
							(aEventState[uEvent].m_eEventState==EVENT_STATE_UNKNOWN || aEventState[uEvent].m_eEventState==EVENT_STATE_SET))
							aEventState[uEvent].m_eEventState=EVENT_STATE_UNKNOWN_CAN_RESET;
						else if (aEventState[uEvent].m_uTotSetting>0)
							aEventState[uEvent].m_eEventState=EVENT_STATE_UNKNOWN;

						++aEventState[uEvent].m_uUnsetConter;
						++aEventState[uEvent].m_uTotUnsetting;
						break;
					}

					case EventFunctionalSharedData::UNSET_EVENT_END:
					{
						NJ_TEST(aEventState[uEvent].m_uTotUnsetting>0);
						--aEventState[uEvent].m_uTotUnsetting;

						if (aEventState[uEvent].m_eEventState==EVENT_STATE_UNKNOWN_CAN_RESET)
							aEventState[uEvent].m_eEventState=EVENT_STATE_UNSET;
						break;
					}

					default:
						NJ_TEST_FAIL();
				}
				++pEntry;
			}

			pEventPos=pSharedData->m_aEvent;

			while (pEventPos<pEventEnd)
			{
				NjDelete(*pEventPos);
				++pEventPos;
			}

			NjDelete(pSharedData);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
void NjEventTest::Static::RunEventPerformances()
{
	NJ_TEST_UNITS_GROUP(Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Constructors, Destructors (performances)");
			static const NjUint OPERATIONS = 4000000*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
			static const NjUint ITERATIONS = 200*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;

			NjUint * const pOperations(NjNewArray(NjUint[ITERATIONS]));

			NjUint uTotOperations(OPERATIONS);
			NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, 21);
			NjUint uIteration(ITERATIONS);
			NjUint uMaxOperations(0);
			while (uIteration>0)
			{
				NjUint uOperations(uTotOperations/uIteration);
				--uIteration;

				if (uIteration>0)
					uOperations+=(NjInt(uOperations)*NjInt(kRand()-10))/100;

				if (uOperations>uMaxOperations)
					uMaxOperations=uOperations;

				pOperations[uIteration]=uOperations;
				uTotOperations-=uOperations;
			}

			NjUint8 * const pBuffer(NjNewArray(NjUint8[(sizeof(NjEvent)+sizeof(NjPlatformSyncObject::SyncObject))*uMaxOperations]));
			NjEvent * const pEvents(reinterpret_cast<NjEvent *>(pBuffer));
			NjPlatformSyncObject::SyncObject * const pPlatformSyncObjects(reinterpret_cast<NjPlatformSyncObject::SyncObject *>(pEvents+uMaxOperations));

			NjHighResolutionTimer kPlatformSyncObjectCreate;
			NjHighResolutionTimer kPlatformSyncObjectDestroy;
			NjHighResolutionTimer kEventCreateSet;
			NjHighResolutionTimer kEventCreateUnset;
			NjHighResolutionTimer kEventDestroy;
			NjHighResolutionTimer kOverHead;

			NjUint const * pPos(pOperations);
			NjUint const * const pEnd(pOperations+ITERATIONS);

			while (pPos!=pEnd)
			{
				NjUint const uOperations(*pPos);
				NjEvent * pEventPos(pEvents);
				NjEvent const * const pEventEnd(pEvents+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uOperations);

				kOverHead.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					++pPlatformPos;
				}
				kOverHead.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectCreate.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Initialize(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectCreate.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectDestroy.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Destroy(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectDestroy.Pause();

				kEventCreateUnset.Start();
				while (pEventPos<pEventEnd)
				{
					NjPlacementNew kPlacement(pEventPos);
					NJ_PLACEMENT_NEW(kPlacement, NjEvent(NJ_FALSE));
					++pEventPos;
				}
				kEventCreateUnset.Pause();

				pEventPos=pEvents;
				kEventDestroy.Start();
				while (pEventPos<pEventEnd)
				{
					pEventPos->~NjEvent();
					++pEventPos;
				}
				kEventDestroy.Pause();

				pEventPos=pEvents;
				kEventCreateSet.Start();
				while (pEventPos<pEventEnd)
				{
					NjPlacementNew kPlacement(pEventPos);
					NJ_PLACEMENT_NEW(kPlacement, NjEvent(NJ_TRUE));
					++pEventPos;
				}
				kEventCreateSet.Pause();

				pEventPos=pEvents;
				while (pEventPos<pEventEnd)
				{
					pEventPos->~NjEvent();
					++pEventPos;
				}

				++pPos;
			}

			NjDeleteArray(pBuffer);
			NjDeleteArray(pOperations);

			NjFloat fOverHead(kOverHead.GetTimeS());
			NjFloat fPlatformSyncObjectCreate(kPlatformSyncObjectCreate.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectDestroy(kPlatformSyncObjectDestroy.GetTimeS()-fOverHead);
			NjFloat fEventCreateSet(kEventCreateSet.GetTimeS()-fOverHead);
			NjFloat fEventCreateUnset(kEventCreateUnset.GetTimeS()-fOverHead);
			NjFloat fEventDestroy(kEventDestroy.GetTimeS()-fOverHead);

			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::CREATION, OPERATIONS, fPlatformSyncObjectCreate);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::CREATION, OPERATIONS, fEventCreateSet);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::CREATION, OPERATIONS, fEventCreateUnset);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::DESCTURCTION, OPERATIONS, fPlatformSyncObjectDestroy);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::DESCTURCTION, OPERATIONS, fEventDestroy);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::CLASS_SIZE, 1, sizeof(NjPlatformSyncObject::SyncObject));
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::CLASS_SIZE, 1, sizeof(NjEvent));
		NJ_TEST_UNIT_END();

		NJ_TEST_UNIT("Set, Unset (performances)");
			static const NjUint OPERATIONS = 4000000*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
			static const NjUint ITERATIONS = 200*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
			static NjBool s_bResult=NJ_FALSE;

			NjUint * const pOperations(NjNewArray(NjUint[ITERATIONS]));

			NjUint uTotOperations(OPERATIONS);
			NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, 21);
			NjUint uIteration(ITERATIONS);
			NjUint uMaxOperations(0);
			while (uIteration>0)
			{
				NjUint uOperations(uTotOperations/uIteration);
				--uIteration;

				if (uIteration>0)
					uOperations+=(NjInt(uOperations)*NjInt(kRand()-10))/100;

				if (uOperations>uMaxOperations)
					uMaxOperations=uOperations;

				pOperations[uIteration]=uOperations;
				uTotOperations-=uOperations;
			}

			NjUint8 * const pBuffer(NjNewArray(NjUint8[(sizeof(NjEvent)+sizeof(NjPlatformSyncObject::SyncObject)*2)*uMaxOperations]));
			NjEvent * const pEvents(reinterpret_cast<NjEvent *>(pBuffer));
			NjPlatformSyncObject::SyncObject * const pPlatformSyncObjects(reinterpret_cast<NjPlatformSyncObject::SyncObject *>(pEvents+uMaxOperations));

			SharedData02 kSharedData;
			kSharedData.m_pPlatformSync=pPlatformSyncObjects+uMaxOperations;
			kSharedData.m_uTotal=uMaxOperations;

			{
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uMaxOperations*2);
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Initialize(*pPlatformPos);
					++pPlatformPos;
				}

				NjEvent * pEventPos(pEvents);
				NjEvent const * const pEventEnd(pEvents+uMaxOperations);
				while (pEventPos<pEventEnd)
				{
					NjPlacementNew kPlacement(pEventPos);
					NJ_PLACEMENT_NEW(kPlacement, NjEvent(NJ_TRUE));
					++pEventPos;
				}
			}

			NjThreadsSystem::Thread * const pThread(NjThreadsSystem::CreateThread(SharedData02::ThreadFunct, &kSharedData));
			NJ_TEST(pThread!=NJ_NULL);
			kSharedData.m_kWaitLock.Lock();

			NjHighResolutionTimer kPlatformSyncObjectLock;
			NjHighResolutionTimer kPlatformSyncObjectTryLockSuccess;
			NjHighResolutionTimer kPlatformSyncObjectTryLockFail;
			NjHighResolutionTimer kPlatformSyncObjectUnlock;
			NjHighResolutionTimer kEventSet;
			NjHighResolutionTimer kEventTryWaitSuccess;
			NjHighResolutionTimer kEventTryWaitFail;
			NjHighResolutionTimer kEventUnset;
			NjHighResolutionTimer kOverHead;

			NjUint const * pPos(pOperations);
			NjUint const * const pEnd(pOperations+ITERATIONS);

			while (pPos!=pEnd)
			{
				NjUint const uOperations(*pPos);
				NjEvent * pEventPos(pEvents);
				NjEvent const * const pEventEnd(pEvents+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformTryLockFailPos(pPlatformSyncObjects+uMaxOperations);
				NjPlatformSyncObject::SyncObject const * const pPlatformTryLockFailEnd(pPlatformTryLockFailPos+uOperations);

				kOverHead.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					++pPlatformPos;
				}
				kOverHead.Pause();

				pPlatformPos=pPlatformTryLockFailPos;
				kPlatformSyncObjectTryLockFail.Start();
				while (pPlatformPos<pPlatformTryLockFailEnd)
				{
					s_bResult=NjPlatformSyncObject::TryLock(*pPlatformPos, 0);
					++pPlatformPos;
				}
				kPlatformSyncObjectTryLockFail.Pause();
				
				NJ_TEST(!s_bResult);

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectLock.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Lock(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectLock.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectUnlock.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Unlock(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectUnlock.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectTryLockSuccess.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					s_bResult=NjPlatformSyncObject::TryLock(*pPlatformPos, 0);
					++pPlatformPos;
				}
				kPlatformSyncObjectTryLockSuccess.Pause();

				NJ_TEST(s_bResult);

				pPlatformPos=pPlatformSyncObjects;
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Unlock(*pPlatformPos);
					++pPlatformPos;
				}

				kEventUnset.Start();
				while (pEventPos<pEventEnd)
				{
					pEventPos->Unset();
					++pEventPos;
				}
				kEventUnset.Pause();

				pEventPos=pEvents;
				kEventTryWaitFail.Start();
				while (pEventPos<pEventEnd)
				{
					s_bResult=pEventPos->TryWait(0);
					++pEventPos;
				}
				kEventTryWaitFail.Pause();

				NJ_TEST(!s_bResult);

				pEventPos=pEvents;
				kEventSet.Start();
				while (pEventPos<pEventEnd)
				{
					pEventPos->Set();
					++pEventPos;
				}
				kEventSet.Pause();

				pEventPos=pEvents;
				kEventTryWaitSuccess.Start();
				while (pEventPos<pEventEnd)
				{
					s_bResult=pEventPos->TryWait(0);
					++pEventPos;
				}
				kEventTryWaitSuccess.Pause();

				NJ_TEST(s_bResult);

				++pPos;
			}

			kSharedData.m_kStartUnlock.Unlock();
			NJ_TEST(NjThreadsSystem::JoinThread(pThread));
			NJ_TEST(NjThreadsSystem::DestroyThread(pThread));

			{
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uMaxOperations*2);
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Destroy(*pPlatformPos);
					++pPlatformPos;
				}

				NjEvent * pEventPos(pEvents);
				NjEvent const * const pEventEnd(pEvents+uMaxOperations);
				while (pEventPos<pEventEnd)
				{
					pEventPos->~NjEvent();
					++pEventPos;
				}
			}

			NjDeleteArray(pBuffer);
			NjDeleteArray(pOperations);

			NjFloat fOverHead(kOverHead.GetTimeS());
			NjFloat fPlatformSyncObjectLock(kPlatformSyncObjectLock.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectTryLockSuccess(kPlatformSyncObjectTryLockSuccess.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectTryLockFail(kPlatformSyncObjectTryLockFail.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectUnlock(kPlatformSyncObjectUnlock.GetTimeS()-fOverHead);
			NjFloat fEventSet(kEventSet.GetTimeS()-fOverHead);
			NjFloat fEventUnset(kEventUnset.GetTimeS()-fOverHead);
			NjFloat fEventTryWaitSuccess(kEventTryWaitSuccess.GetTimeS()-fOverHead);
			NjFloat fEventTryWaitFail(kEventTryWaitFail.GetTimeS()-fOverHead);

			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_UNLOCK, OPERATIONS, fPlatformSyncObjectUnlock);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_SET, OPERATIONS, fEventSet);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_LOCK, OPERATIONS, fPlatformSyncObjectLock);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_UNSET, OPERATIONS, fEventUnset);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_TRY_LOCK_SUCCESS, OPERATIONS, fPlatformSyncObjectTryLockSuccess);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_TRY_WAIT_SUCCESS, OPERATIONS, fEventTryWaitSuccess);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_TRY_LOCK_FAIL, OPERATIONS, fPlatformSyncObjectTryLockFail);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_TRY_WAIT_FAIL, OPERATIONS, fEventTryWaitFail);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
void NjEventTest::Static::RunEventAssert()
{
#if NJ_EVENT_STORE_USERS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
	NJ_TEST_UNITS_GROUP(Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Assertion (destruction)");
			NjSmartPtr<NjDebugSystem::Implementation> spImplementation(NjDebugSystem::GetImplementation());
			NjDebugSystemTestImplementation * const pTestBreakpoint(NjNew(NjDebugSystemTestImplementation(spImplementation)));
			NJ_TEST(pTestBreakpoint!=NJ_NULL);
			NjDebugSystem::SetImplementation(pTestBreakpoint);

			NjThreadsSystem::Thread * pThread;

			{
				NjEvent kSyncObject(NJ_FALSE);
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());

				pThread=NjThreadsSystem::CreateThread(RunEventAssertThread, &kSyncObject);

				NJ_TEST(pThread!=NJ_NULL);
				NJ_TEST(NjThreadsSystem::Sleep(NjSharedSyncObjectConstants::TIME_GRANULARITY));
			}

			NJ_TEST(pTestBreakpoint->WasTriggeredBreakpoint());

			NJ_TEST(NjThreadsSystem::TryJoinThread(pThread, NjSharedSyncObjectConstants::TIME_GRANULARITY*5)==NjEventsSystem::TRY_SUCCESS);
			NJ_TEST(NjThreadsSystem::DestroyThread(pThread));

			NjDebugSystem::SetImplementation(spImplementation);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
#endif // NJ_EVENT_STORE_USERS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
}
//----------------------------------------------------------------------------
void NjEventTest::Static::RunEventFunctionalThread(void * pData)
{
	EventFunctionalSharedData::ThreadData & kThreadData(*reinterpret_cast<EventFunctionalSharedData::ThreadData *>(pData));
	EventFunctionalSharedData & kSharedData(*kThreadData.m_pSharedData);
	NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, 1000000);

	NjUint uIterations(EventFunctionalSharedData::ITERATIONS);

	while (uIterations>0)
	{
		--uIterations;

		NjUint const uEvent(kRand()%EventFunctionalSharedData::TOT_EVENTS);
		NjEvent * const pEvent(kSharedData.m_aEvent[uEvent]);
		NjUint const uAction(kRand()%3);

		NjUint uIndex(kSharedData.m_kTotEntries.PostIncrement());
		EventFunctionalSharedData::Entry * pEntry(kSharedData.m_aEntries+uIndex);
		pEntry->m_uEvent=uEvent;
		pEntry->m_pThreadData=&kThreadData;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
		NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pEvent, sizeof(NjEvent));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT

		EventFunctionalSharedData::EventState eNextState;

		if (uAction==0)
		{
			pEntry->m_eEventState=EventFunctionalSharedData::TRY_WAIT_START;
			if (pEvent->TryWait(NjSharedSyncObjectConstants::TIME_GRANULARITY))
				eNextState=EventFunctionalSharedData::TRY_WAIT_END;
			else
				eNextState=EventFunctionalSharedData::TRY_WAIT_FAIL;
		}
		else if (uAction==1)
		{
			pEntry->m_eEventState=EventFunctionalSharedData::UNSET_EVENT_START;
			pEvent->Unset();
			eNextState=EventFunctionalSharedData::UNSET_EVENT_END;
		}
		else
		{
			NJ_ASSERT(uAction==2);
			pEntry->m_eEventState=EventFunctionalSharedData::SET_EVENT_START;
			pEvent->Set();
			eNextState=EventFunctionalSharedData::SET_EVENT_END;
		}

		uIndex=kSharedData.m_kTotEntries.PostIncrement();
		pEntry=kSharedData.m_aEntries+uIndex;
		pEntry->m_uEvent=uEvent;
		pEntry->m_pThreadData=&kThreadData;
		pEntry->m_eEventState=eNextState;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
		NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pEvent, sizeof(NjEvent));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
	}

	kThreadData.m_eErrorState=STATUS_SUCCESS;
}
//----------------------------------------------------------------------------
void NjEventTest::Static::RunEventAssertThread(void * pData)
{
	NjEvent * const pSyncObject(reinterpret_cast<NjEvent *>(pData));
	pSyncObject->TryWait(NjSharedSyncObjectConstants::TIME_GRANULARITY*2);
}
//----------------------------------------------------------------------------
void NjEventTest::Static::SharedData02::ThreadFunct(void * pData)
{
	SharedData02 * const pSharedData(reinterpret_cast<SharedData02 *>(pData));
	if (pSharedData==NJ_NULL)
		return;

	NjPlatformSyncObject::SyncObject * pPlatformPos(pSharedData->m_pPlatformSync);
	NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+pSharedData->m_uTotal);
	while (pPlatformPos<pPlatformEnd)
	{
		NjPlatformSyncObject::Lock(*pPlatformPos);
		++pPlatformPos;
	}

	pSharedData->m_kWaitLock.Unlock();
	pSharedData->m_kStartUnlock.Lock();

	pPlatformPos=pSharedData->m_pPlatformSync;
	while (pPlatformPos<pPlatformEnd)
	{
		NjPlatformSyncObject::Unlock(*pPlatformPos);
		++pPlatformPos;
	}
}
//----------------------------------------------------------------------------
